Amazon S3 でバケット内の空フォルダを一覧化するには
困っていた内容
下記を参考にアカウント間でバケットのデータをコピーしましたが、コピー元とコピー先でオブジェクト数に差異が出ています。
参考:別の AWS アカウントから S3 オブジェクトをコピーするにはどうすればよいですか?
原因はコピーの際に空フォルダが消えてしまっているためでした。空フォルダを一覧化する、または空フォルダもコピーする方法はありますか?
どう対応すればいいの?
S3における空フォルダとは
まずS3の基本的な構造の話ですが、そもそもS3においてフォルダやディレクトリという概念は存在しません。例えばフルパスが「foo/bar.txt」の場合、フォルダ(foo)とファイル(bar.txt)ではなく、「foo/bar.txt」というキーの1つのオブジェクトということになります。
ただし、S3管理コンソールでは、人間に分かりやすいようスラッシュ区切りのディレクトリ階層とすることで、フォルダやファイルのように表示されており、コンソール上で「フォルダの作成」をした場合には、S3は明示的に0バイトの空フォルダを作って表現します。
詳しい話については、下記ブログをご確認ください。
以下、試しに「test1」というフォルダをコンソールで作成し、他のオブジェクトはCLIでアップロードしました。(lsの結果、空フォルダとして0バイトで存在する「test1/」に対し、「test2/」や「test3/」は単体では表示されていないことが分かります。)
cp
や sync
といったコピーリクエストを試してみましたが、0バイトのオブジェクトはコピーされても、空フォルダ「test1/」がコピーされることはありません。
% aws s3 ls s3://ito-test-0528 --recursive --sum 2021-05-28 18:45:11 0 empty.out 2021-05-31 11:26:48 1024 test.txt 2021-05-28 18:26:58 0 test1/ 2021-05-31 11:33:39 1024 test1/test.txt 2021-05-31 11:25:21 454 test2/test.html 2021-05-28 18:46:47 0 test3/empty.out Total Objects: 6 Total Size: 2502 % aws s3 sync s3://ito-test-0528 s3://ito-test-0528-copy % aws s3 ls s3://ito-test-0528-copy --recursive --sum 2021-05-28 18:49:13 0 empty.out 2021-05-31 11:27:45 1024 test.txt 2021-05-31 11:34:00 1024 test1/test.txt 2021-05-31 11:27:45 454 test2/test.html 2021-05-28 18:49:13 0 test3/empty.out Total Objects: 5 Total Size: 2502 % aws s3 cp s3://ito-test-0528/test1 s3://ito-test-0528-copy --recursive copy: s3://ito-test-0528/test1/test.txt to s3://ito-test-0528-copy/test.txt # コピー先バケットのlsの結果は上記と変わらず
なお、確かにオブジェクト数は異なりますがトータルサイズは同じなので、もし0バイトのファイルがコピーされたかどうかを確認する必要がなければ、こちらの数値で比較すれば済みそうですね。
空フォルダの一覧化
空フォルダの数を確かめる必要がある場合、下記のように jq
を活用して簡単に一覧化できます。
aws s3api list-objects --bucket BUCKETNAME | jq -r '.Contents[] | select(.Key | endswith("/")) | select(.Size == 0) | .Key'
今回の例でさらにフォルダ「test4/」とその配下の「test4-1/」もコンソールから追加してみました。そして上記コマンドを実行すると、以下のように空フォルダのオブジェクトだけ抽出することができました。
% aws s3 ls s3://ito-test-0528 --recursive 2021-05-28 18:45:11 0 empty.out 2021-05-31 11:26:48 1024 test.txt 2021-05-28 18:26:58 0 test1/ 2021-05-31 11:33:39 1024 test1/test.txt 2021-05-31 11:25:21 454 test2/test.html 2021-05-28 18:46:47 0 test3/empty.out 2021-05-31 12:06:41 0 test4/ 2021-05-31 12:06:57 0 test4/test4-1/ % aws s3api list-objects --bucket ito-test-0528 | jq -r '.Contents[] | select(.Key | endswith("/")) | select(.Size == 0) | .Key' test1/ test4/ test4/test4-1/
jq とは
jq は、JSONデータを整形するために最適なツールです。AWS CLI の結果はJSONで返されますので、必要な値を抽出したりデータを集計したりするのに活用されています。
こちらの公式サイトより各種OSへインストールしてご利用ください。MacならHomebrewより入れられます。
- インストールのご参考:Windowsでjqコマンドを導入してJSONレスポンスを整形・抽出する | Zenn
- jq の使い方:
- AWS CLI への活用例:
本当に空フォルダはコピーできないのか?
上述で空フォルダのコピーができないとお見せしましたが、念のため他の方法も試してみました。cp
で指定を工夫しましたがエラーとなり、ローカル(フォルダ「test5」)からも試しましたがやはり sync
では空フォルダは無視され、cp
なら同様のエラーとなりました。
% aws s3 cp s3://ito-test-0528/test1 s3://ito-test-0528-copy fatal error: An error occurred (404) when calling the HeadObject operation: Key "test1" does not exist % aws s3 cp s3://ito-test-0528/test1/ s3://ito-test-0528-copy copy failed: s3://ito-test-0528/test1/ to s3://ito-test-0528-copy/ Parameter validation failed: Invalid length for parameter Key, value: 0, valid range: 1-inf % tree test5 test5 ├── test.txt ├── test1 │ └── test.txt └── test2 % aws s3 cp test5 s3://ito-test-0528 --recursive upload: test5/test.txt to s3://ito-test-0528/test.txt upload: test5/test1/test.txt to s3://ito-test-0528/test1/test.txt
調べた限り、GitHub イシューで何年も前から要望されていますが、現時点では導入されていないようです。
なお、CLIでは試した&調べた限り空フォルダのコピーはできないようですが、SDK では実現する方法があるという情報がいくつかのサイトに載っていましたので、ユースケース上どうしても必要な場合は調べて参照してみてください。(※動作未確認です)
- https://stackoverflow.com/questions/59952936/how-to-create-an-empty-folder-in-aws-s3-bucket-with-using-android-sdk
- https://stackoverflow.com/questions/1939743/amazon-s3-boto-how-to-create-a-folder
参考資料
"名前が空の"フォルダを作ってしまった場合には:
バケットに大量オブジェクトがある場合、リストする際は料金にご注意ください: